﻿using System;
using System.Linq;

namespace SoftSource.Reactive
{
    public static class ReactiveExtensions
    {
        /// <summary>
        /// If the IObservable is null, then return the default IObservable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="observable"></param>
        /// <returns></returns>
        public static IObservable<T> Nullable<T>(this IObservable<T> observable)
        {
            if (observable == null)
            {
                return Observable.Return<T>(default(T));
            }
            else
            {
                return observable;
            }
        }

        /// <summary>
        /// Changes the type to Unit
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="observable"></param>
        /// <returns></returns>
        public static IObservable<Unit> ToUnit<T>(this IObservable<T> observable)
        {
            return observable.Select(value => new Unit());
        }

        /// <summary>
        /// Creates an IObservable of type Unit that returns exactly once.
        /// This is useful for load type queries that should only be executed once.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="observable"></param>
        /// <returns></returns>
        public static IObservable<Unit> Once<T>(this IObservable<T> observable)
        {
            var pruned = observable.Prune();
            pruned.Connect();
            return pruned.ToUnit();
        }

        /// <summary>
        /// Each subscriber will be queued until the previous observable has completed.
        /// </summary>
        /// <typeparam name="T">The type of observable</typeparam>
        /// <param name="observable">The observabe to queue subscriptions against</param>
        /// <returns>An observable that queues subscriptions to the underlying observable</returns>
        public static IObservable<T> QueueSubscribers<T>(this IObservable<T> observable)
        {
            var queue = new ObservableQueue<T>();

            // Every time someone subscribes, queue the operation
            return Observable.CreateWithDisposable<T>(observer =>
                queue.QueueObservable(observable).Subscribe(observer));
        }

        /// <summary>
        /// Defers subscribers until the observable has been set.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="observable"></param>
        /// <returns></returns>
        public static IDeferredObservable<T> Defer<T>(this IObservable<T> observable)
        {
            return new DeferredObservable<T>(observable.Subscribe);
        }
    }
}
